﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public delegate T EnumerableTransformAction<S, T>(S s);
public delegate bool EnumerableFilterAction<T>(T t);
public delegate int EnumerableCompareAction<T>(T t1, T t2);
public delegate void EnumerableAction<T>(T t);
public delegate void EnumerableActionCanBreak<T>(T t, ref bool _break);

public static class Extends
{
    public static IEnumerable<T> Transform<S, T>(this IEnumerable<S> e, EnumerableTransformAction<S, T> action)
    {
        HashSet<T> ret = new HashSet<T>();
        IEnumerator<S> enumerator = e.GetEnumerator();
        while (enumerator.MoveNext())
        {
            ret.Add(action(enumerator.Current));
        }
        return ret;
    }

    public static IEnumerable<T> Filter<T>(this IEnumerable<T> e, EnumerableFilterAction<T> action)
    {
        HashSet<T> ret = new HashSet<T>();
        IEnumerator<T> enumerator = e.GetEnumerator();
        while (enumerator.MoveNext())
        {
            if (action(enumerator.Current))
            {
                ret.Add(enumerator.Current);
            }
        }
        return ret;
    }

    public static T FindWith<T>(this IEnumerable<T> e, EnumerableFilterAction<T> action)
    {
        T ret = default;
        if (e != null)
        {
            IEnumerator<T> enumerator = e.GetEnumerator();
            while (enumerator.MoveNext())
            {
                if (action(enumerator.Current))
                {
                    ret = enumerator.Current;
                    break;
                }
            }
        }
        return ret;
    }

    public static T Min<T>(this IEnumerable<T> e, EnumerableCompareAction<T> action)
    {
        T ret = default;
        if (e != null)
        {
            IEnumerator<T> enumerator = e.GetEnumerator();
            bool first = true;
            while (enumerator.MoveNext())
            {
                if (first || action(enumerator.Current, ret) < 0)
                {
                    ret = enumerator.Current;
                }
                first = false;
            }
        }
        return ret;
    }

    public static T Max<T>(this IEnumerable<T> e, EnumerableCompareAction<T> action)
    {
        T ret = default;
        if (e != null)
        {
            IEnumerator<T> enumerator = e.GetEnumerator();
            bool first = true;
            while (enumerator.MoveNext())
            {
                if (first || action(enumerator.Current, ret) > 0)
                {
                    ret = enumerator.Current;
                }
                first = false;
            }
        }
        return ret;


    }

    public static IEnumerable<T> Foreach<T>(this IEnumerable<T> e, EnumerableAction<T> action)
    {
        return e.Foreach((T t, ref bool _break) =>
        {
            action(t);
        });
    }
    public static IEnumerable<T> Foreach<T>(this IEnumerable<T> e, EnumerableActionCanBreak<T> action)
    {
        if (e == null)
        {
            Debug.LogError("element in IEnumerable is Null");
        }
        else
        {
            IEnumerator<T> enumerator = e.GetEnumerator();
            bool _break = false;
            while (enumerator.MoveNext())
            {
                if (_break)
                {
                    break;
                }
                action(enumerator.Current, ref _break);
            }
        }
        return e;
    }

    public static bool Empty(this IEnumerable e)
    {
        IEnumerator enumerator = e.GetEnumerator();
        bool ret = !enumerator.MoveNext();
        return ret;
    }

    public static T[] ToArray<T>(this IEnumerable<T> e)
    {
        List<T> ret = new();
        IEnumerator<T> enumerator = e.GetEnumerator();
        while (enumerator.MoveNext())
        {
            ret.Add(enumerator.Current);
        }
        return ret.ToArray();
    }
}

public static class ExtendLookAtDirection

{

    public static Vector2 ToVector2(this LookAtDirection8 lookAt)
    {
        Vector2 ret = Vector2.zero;
        switch (lookAt)
        {
            case LookAtDirection8.Left:
                ret = Vector2.left;
                break;
            case LookAtDirection8.LeftUp:
                ret = Vector2.left + Vector2.up;
                break;
            case LookAtDirection8.Up:
                ret = Vector2.up;
                break;
            case LookAtDirection8.RightUp:
                ret = Vector2.right + Vector2.up;
                break;
            case LookAtDirection8.Right:
                ret = Vector2.right;
                break;
            case LookAtDirection8.RightDown:
                ret = Vector2.right + Vector2.down;
                break;
            case LookAtDirection8.Down:
                ret = Vector2.down;
                break;
            case LookAtDirection8.LeftDown:
                ret = Vector2.left + Vector2.down;
                break;
        }
        return ret;
    }

    public static Vector2 ToVector2(this LookAtDirection12 lookAt)
    {
        Vector2 ret = Vector2.zero;
        switch (lookAt)
        {
            case LookAtDirection12.Left:
                ret = Vector2.left;
                break;
            case LookAtDirection12.LeftLeftUp:
                ret = Quaternion.AngleAxis(-30, Vector3.forward) * Vector2.left;
                break;
            case LookAtDirection12.UpLeftUp:
                ret = Quaternion.AngleAxis(-60, Vector3.forward) * Vector2.left;
                break;
            case LookAtDirection12.Up:
                ret = Vector2.up;
                break;
            case LookAtDirection12.UpRightUp:
                ret = Quaternion.AngleAxis(-30, Vector3.forward) * Vector2.up;
                break;
            case LookAtDirection12.RightRightUp:
                ret = Quaternion.AngleAxis(-60, Vector3.forward) * Vector2.up;
                break;
            case LookAtDirection12.Right:
                ret = Vector2.right;
                break;
            case LookAtDirection12.RightRightDown:
                ret = Quaternion.AngleAxis(-30, Vector3.forward) * Vector2.right;
                break;
            case LookAtDirection12.DownRightDown:
                ret = Quaternion.AngleAxis(-60, Vector3.forward) * Vector2.right;
                break;
            case LookAtDirection12.Down:
                ret = Vector2.down;
                break;
            case LookAtDirection12.DownLeftDown:
                ret = Quaternion.AngleAxis(-30, Vector3.forward) * Vector2.down;
                break;
            case LookAtDirection12.LeftLeftDown:
                ret = Quaternion.AngleAxis(-60, Vector3.forward) * Vector2.down;
                break;
        }
        return ret;
    }
}

public static class ExtendVector2
{
    public static LookAtDirection8 ToLookAtDirection8(this Vector2 lookAt)
    {
        Vector2 look = lookAt;
        look.Normalize();
        look.Set(Mathf.Round(look.x), Mathf.Round(look.y));

        LookAtDirection8 ret = LookAtDirection8.None;
        if (look.x == 0 && look.y == 0)
        {
            ret = LookAtDirection8.None;
        }
        else if (look.x == 0 && look.y == 1)
        {
            ret = LookAtDirection8.Up;
        }
        else if (look.x == 0 && look.y == -1)
        {
            ret = LookAtDirection8.Down;
        }
        else if (look.x == 1 && look.y == 0)
        {
            ret = LookAtDirection8.Right;
        }
        else if (look.x == -1 && look.y == 0)
        {
            ret = LookAtDirection8.Left;
        }
        else if (look.x == -1 && look.y == 1)
        {
            ret = LookAtDirection8.LeftUp;
        }
        else if (look.x == -1 && look.y == -1)
        {
            ret = LookAtDirection8.LeftDown;
        }
        else if (look.x == 1 && look.y == 1)
        {
            ret = LookAtDirection8.RightUp;
        }
        else if (look.x == 1 && look.y == -1)
        {
            ret = LookAtDirection8.RightDown;
        }
        return ret;
    }

    public static LookAtDirection12 ToLookAtDirection12(this Vector2 lookAt)
    {
        LookAtDirection12 ret = LookAtDirection12.None;
        float angle = Vector2.SignedAngle(Vector2.left, lookAt);
        if (angle < 15 && angle >= -15)
        {
            ret = LookAtDirection12.Left;
        }
        else if (angle < 0 && angle >= -45)
        {
            ret = LookAtDirection12.LeftLeftUp;
        }
        else if (angle < 0 && angle >= -75)
        {
            ret = LookAtDirection12.UpLeftUp;
        }
        else if (angle < 0 && angle >= -105)
        {
            ret = LookAtDirection12.Up;
        }
        else if (angle < 0 && angle >= -135)
        {
            ret = LookAtDirection12.UpRightUp;
        }
        else if (angle < 0 && angle >= -165)
        {
            ret = LookAtDirection12.RightRightUp;
        }
        else if ((angle < 0 && angle >= -180) || angle >= 165)
        {
            ret = LookAtDirection12.Right;
        }
        else if (angle >= 135)
        {
            ret = LookAtDirection12.RightRightDown;
        }
        else if (angle >= 105)
        {
            ret = LookAtDirection12.DownRightDown;
        }
        else if (angle >= 75f)
        {
            ret = LookAtDirection12.Down;
        }
        else if (angle >= 45)
        {
            ret = LookAtDirection12.DownLeftDown;
        }
        else if (angle >= 15)
        {
            ret = LookAtDirection12.LeftLeftDown;
        }

        return ret;

    }
}
